package main

import (
	"context"
	"fmt"
	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/container"
	"github.com/docker/docker/api/types/filters"
	"github.com/docker/docker/client"
	. "go_learn/constant"
	"io/ioutil"
	"log"
	"strconv"
	"strings"
)

func CreateEtcdCluster(hosts []string, image string) (string, error) {
	ctx := context.Background()
	// 需要挂载的目录
	MOUNT_DIR := []string{"/etc/kubernetes/ssl:/etc/kubernetes/ssl", "/data/etcd:/var/lib/etcd"}

	//每个主节点同时也是etcd节点
	var ipEtcdName map[string]string
	ipEtcdName = make(map[string]string)

	//generate: initalCluster etcd0=http://192.168.2.55:2380, etcd1=http://192.168.2.54:2380 ,etcd2=http://192.168.2.56:2380
	var initialCluster string
	for index, hostIP := range hosts {
		fmt.Println(hostIP, index)
		ipEtcdName[hostIP] = "etcd" + strconv.Itoa(index)
		initialCluster += "etcd" + strconv.Itoa(index) + "=https://" + hostIP + ":2380,"
	}
	initialCluster = strings.TrimSuffix(initialCluster, ",")
	log.Println(initialCluster)

	//遍历每个master节点，创建etcd集群
	for index, nodeIp := range hosts {
		if nodeIp == "47.75.175.107" {
			nodeIp = "172.31.146.1"
		}
		host := "http://" + nodeIp + DOCKER_API_PORT
		cli, err := client.NewClient(host, DOCKER_API_VERSION, nil, nil)
		if err != nil {
			return "", err
		}
		CheckImage(image, cli)

		config := &container.Config{
			Image: image,
			Cmd: []string{
				"/usr/local/bin/etcd",
				"--name",
				ipEtcdName[nodeIp],
				"--cert-file=/etc/kubernetes/ssl/kubernetes.pem",
				"--key-file=/etc/kubernetes/ssl/kubernetes-key.pem",
				"--peer-cert-file=/etc/kubernetes/ssl/kubernetes.pem",
				"--peer-key-file=/etc/kubernetes/ssl/kubernetes-key.pem",
				"--trusted-ca-file=/etc/kubernetes/ssl/ca.pem",
				"--peer-trusted-ca-file=/etc/kubernetes/ssl/ca.pem",
				"--initial-advertise-peer-urls",
				"https://" + nodeIp + ":2380",
				"--listen-client-urls",
				"https://" + nodeIp + ":2379,http://127.0.0.1:2379",
				"--listen-peer-urls",
				"https://" + nodeIp + ":2380",
				"--advertise-client-urls",
				"https://" + nodeIp + ":2379",
				"--initial-cluster-token",
				"etcd-cluster",
				"--initial-cluster",
				initialCluster,
				"--initial-cluster-state",
				"new",
				"--data-dir=/var/lib/etcd"},
		}
		var restartPolicy = &container.RestartPolicy{
			Name: "always",
		}
		//ctx context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, containerName string) (container.ContainerCreateCreatedBody, error
		resp, err := cli.ContainerCreate(
			ctx,
			config,
			&container.HostConfig{
				Binds:         MOUNT_DIR,
				Privileged:    true,
				NetworkMode:   "host",
				RestartPolicy: *restartPolicy,
			},
			nil,
			"etcd")

		if err != nil {
			return "", err
		}
		if err := cli.ContainerStart(ctx, resp.ID, types.ContainerStartOptions{}); err != nil {
			return "", err
		}
		log.Println(resp.ID)

		if index == 0 {
			key := "/kube-centos2/network/config"
			value := `{"Network":"172.30.0.0/16","SubnetLen":24,"Backend":{"Type":"vxlan"}}`
			cmd := []string{
				"etcdctl",
				"--endpoints=http://127.0.0.1:2379",
				"--ca-file=/etc/kubernetes/ssl/ca.pem",
				"--cert-file=/etc/kubernetes/ssl/kubernetes.pem",
				"--key-file=/etc/kubernetes/ssl/kubernetes-key.pem",
				"set",
				key,
				value}
			err := DockerExec(cli, resp.ID, cmd)
			if err != nil {
				log.Print("etcd set subnet failure")
				log.Print(err)
				return "", err
			}
		}
	}
	return initialCluster, nil
}

func DockerExec(cli *client.Client, containerId string, cmd []string) error {
	execConfig := types.ExecConfig{Cmd: cmd, AttachStderr: true, AttachStdout: true}

	//execStartCheck := types.ExecStartCheck{}
	execResp, err := cli.ContainerExecCreate(context.Background(), containerId, execConfig)
	if err != nil {
		return err
	} else {
		fmt.Println("execStart")
		return cli.ContainerExecStart(context.Background(), execResp.ID, types.ExecStartCheck{true, true})
	}
}

func clearDir(hostIp string, image string) error {
	ctx := context.Background()
	// 需要删除的目录
	MOUNT_DIR := []string{"/data:/data"}

	host := "http://" + hostIp + DOCKER_API_PORT
	cli, err := client.NewClient(host, DOCKER_API_VERSION, nil, nil)
	if err != nil {
		return err
	}
	CheckImage(image, cli)

	config := &container.Config{
		Image: image,
		Cmd: []string{
			"rm", "-rf", "/data/etcd/"},
	}
	var restartPolicy = &container.RestartPolicy{
		Name: "",
	}
	//ctx context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, containerName string) (container.ContainerCreateCreatedBody, error
	resp, err := cli.ContainerCreate(
		ctx,
		config,
		&container.HostConfig{
			Binds:         MOUNT_DIR,
			Privileged:    true,
			NetworkMode:   "none",
			RestartPolicy: *restartPolicy,
		},
		nil,
		"")

	if err != nil {
		return err
	}
	if err := cli.ContainerStart(ctx, resp.ID, types.ContainerStartOptions{}); err != nil {
		return err
	}
	return nil
}

func main() {
	hosts := []string{"20.26.38.135"}
	image := "registry.yw.zj.chinamobile.com/kubernetes/etcd-amd64:3.1.11"
	err := clearDir("20.26.38.135", image)
	if err != nil {
		fmt.Println(err)
	}
	CreateEtcdCluster(hosts, image)
}

func CheckImage(imageName string, cli *client.Client) {

	filters := filters.NewArgs()
	filters.Add("reference", imageName)

	imageOpt := types.ImageListOptions{
		All:     true,
		Filters: filters,
	}
	images, err := cli.ImageList(context.Background(), imageOpt)
	if err != nil {
		fmt.Errorf(err.Error())
	}
	if len(images) == 0 {
		fmt.Printf("not found %s\r\n", imageName)
		fmt.Printf("begin pull images %s\r\n", imageName)

		resp, err := cli.ImagePull(context.Background(), imageName, types.ImagePullOptions{})
		if err != nil {
			fmt.Println(err)
		}
		body, err := ioutil.ReadAll(resp)
		if err != nil {
			fmt.Println(err)
		}
		fmt.Println(string(body))
	} else {
		for _, image := range images {
			fmt.Println(image.ID, " ", image.RepoTags)
		}
	}
}
